﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Windows;
using System.Windows.Threading;
using EasyStep.Business;
using EasyStep.Business.Constants;
using EasyStep.Business.Interfaces;
using EasyStep.WinForms;
using EasyStep.Common;
using WPF.TestEditor.Properties;

namespace WPF.TestEditor
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        #region consts
        public const string StartupCommandArgs = "startupCommandLineArgs";
        public const string StartupWindow = "startupWindow";
        public const string PathReplacements = "PathReplacements";
        #endregion
       
        protected override void OnStartup(StartupEventArgs e)
        {
            //EnterDebugMode();
            this.ShutdownMode = ShutdownMode.OnLastWindowClose;
            base.OnStartup(e);
            Initialize(e);
        }
        #region App logic
        public void Initialize(StartupEventArgs e)
        {
            DateTime startup = DateTime.Now;
            LogAppState("Application initializing", 1);
            InitAppInformation();
            
            Properties[StartupCommandArgs] = e;
            Properties[PathReplacements] = InitPathReplacements();
            EnsureSettings();
            (Properties[PathReplacements] as Dictionary<string, string>).Add(MainApplicationServiceConstants.FileSystemDataStoragePath, (AppSettings.ResultsFolder));

            RegisterObjectManager();            
            EnsureLogExist();
            RegisterWebDrivers();
            InitStartupArguments(e);
            LogAppState("Application initialized. Show main window", 1);            
            Properties[StartupWindow] = new MainWindow(ContextInstance);
            Window activeWindow = Properties[StartupWindow] as Window;            
            LogAppState(string.Format("Application initialization time {0}ms",(int)((DateTime.Now-startup).TotalMilliseconds)),1);
            if (activeWindow!=null)
                activeWindow.Show();
        }

        

        private void RegisterObjectManager()
        {
            LogAppState("executing objects manager, app services,type finder registrations", 0);
            ObjectManager.Initialize();
            ObjectManager.RegisterInstance<ITestDataContext>(() => new TestDataContext(AppSettings.LogFolder));
            ContextInstance = ObjectManager.GetInstance<ITestDataContext>();
            ContextInstance.Environment = string.IsNullOrEmpty(AppSettings.Environment) ? "" : AppSettings.Environment;
            ContextInstance.PathReplacements = Properties[PathReplacements] as Dictionary<string, string>;
            MainApplicationService.Initialize(ContextInstance.PathReplacements);
            TypeFinder.Initialize(AppSettings.ActionsFolder);
            if (!ContextInstance.ActionManager.GetAll().Any())
            {
                ShowErrorMessage("Can't continue, no one Action been found to work with tests.\r\nCheck that folder contains *.dll with actions:\r\n" + AppSettings.ActionsFolder);
                Application.Current.Shutdown(-5);
            }           
            LogAppState("Objects manager, app services,type finder  have been registered", 1);
        }

        private void EnsureLogExist()
        {
            if (ContextInstance.Log == null) ShowErrorMessage("Null log4net Log object!Check config!");
            else
            {
                var l = ContextInstance.Log.Logger as log4net.Repository.Hierarchy.Logger;
                if (l != null)
                {
                    if (l.Appenders.Count == 0) ShowErrorMessage("No one appender have been found!Check config!");
                }
                else ShowErrorMessage("Null Logger -check config!");
            }
        }
        private void RegisterWebDrivers()
        {
            LogAppState("Executing web drivers registration", 0);
            try
            {
                var browserTypes = TypeFinder.GetDerivedFrom<IWebBrowserProxy>();
                var browsers = new List<IWebBrowserProxy>();
                foreach (Type browser in browserTypes)
                {
                    var bInfo = Activator.CreateInstance(browser) as IWebBrowserProxy;
                    bInfo.Icon = Replace(string.Concat("%root%resources\\", (bInfo.Icon ?? "").TrimStart('\\')), ContextInstance.PathReplacements);
                    //bInfo.Description=??get string data from resource file
                    browsers.Add(bInfo);
                }
                ContextInstance.RegisteredBrowsers = browsers.ToArray();
                LogAppState("Web drivers have been registered:" + ContextInstance.RegisteredBrowsers.Length, 1);
            }
            catch (Exception ex)
            {
                var e = new InvalidDataException("Can't register browsers to test, check that  classes which derived from IWebBrowserProxy are correct.", ex);
                LogException(ex, e.Message);
                ShowErrorMessage(e.Message);
                Application.Current.Shutdown(-10);
            }
        }

        
        private Dictionary<string, string> InitPathReplacements()
        {
            LogAppState("Creating path replacements masks", 0);
            var pathReplacements = new Dictionary<string, string>();
            try
            {
                pathReplacements.Add("%root%", AppDomain.CurrentDomain.BaseDirectory);
                pathReplacements.Add("%appdata%", GetUserAppDataPath());
                pathReplacements.Add("%temp%", System.IO.Path.GetTempPath());
                foreach (var name in Enum.GetNames(typeof(Environment.SpecialFolder)))
                {
                    var t = default(Environment.SpecialFolder);
                    Enum.TryParse(name, out t);
                    try
                    {
                        pathReplacements.Add("%" + name + "%", Environment.GetFolderPath(t));
                    }
                    catch (Exception ex)
                    {
                        LogException(ex, "Can't init path replacement, security error:" + name);
                    }
                }
            }
            catch (System.Security.SecurityException secEx)
            {
                LogException(secEx, "Can't init path replacements, security error");
                ShowErrorMessage("Security exception:" + secEx.Message + "\r\nmore detailed information could be found in log");
            }
            LogAppState("Path replacements have been initialized", 1);
            return pathReplacements;
        }


        private void InitAppInformation()
        {
            LogAppState("executing app information", 0);
            if (!Debugger.IsAttached && Settings.Default.AttachDebugger) EnterDebugMode();
            CurAssembly = Assembly.GetEntryAssembly();//            Assembly.GetExecutingAssembly();
            if (CurAssembly == null) return;
            object[] customAttributes = CurAssembly.GetCustomAttributes(true);
            var cmpAttr = customAttributes.OfType<AssemblyCompanyAttribute>().FirstOrDefault();
            if (cmpAttr != null) CompanyName = cmpAttr.Company; else CompanyName = "ISM-Ukraine";
            var prdAttr = customAttributes.OfType<AssemblyProductAttribute>().FirstOrDefault();
            if (prdAttr != null) ProductName = prdAttr.Product; else ProductName = "EasyStep.TestEditor";
            var verAttr = customAttributes.OfType<AssemblyInformationalVersionAttribute>().FirstOrDefault();
            if (verAttr != null) ProductVersion = verAttr.InformationalVersion; else ProductVersion = "1.0.0.0";
            LogAppState("App information been collected", 0);
        }
        private void InitStartupArguments(StartupEventArgs e)
        {
            var CurrentTest = string.Empty;
            if (e.Args != null && e.Args.Length > 0)
            {
                if (!Debugger.IsAttached)
                {//enable/disable debugger;
                    foreach (var s in e.Args)
                        if ("debug".Equals(s, StringComparison.OrdinalIgnoreCase))
                            EnterDebugMode();

                }

                //automatically set test file
                CurrentTest = e.Args[0];
                if (!Path.IsPathRooted(CurrentTest) && !CurrentTest.StartsWith("%root%"))
                {
                    CurrentTest = string.Concat("%root%", CurrentTest);
                }
                CurrentTest = Replace(CurrentTest, ContextInstance.PathReplacements);
            }
            else if (!string.IsNullOrEmpty(AppSettings.LastOpenedTest))
                CurrentTest = AppSettings.LastOpenedTest;
            if (!string.IsNullOrEmpty(CurrentTest))
            {
                ContextInstance.CurrentProject = Path.GetDirectoryName(CurrentTest);
                ContextInstance.CurrentTest = Path.GetFileName(CurrentTest);
                ContextInstance.Log.Debug("Automatically open test file:" + CurrentTest);
            }
        }
        #endregion

        [DebuggerHidden]
        protected override void OnDeactivated(EventArgs e)
        {
            base.OnDeactivated(e);
            //bg mode
        }
        [DebuggerHidden]
        protected override void OnActivated(EventArgs e)
        {
            base.OnActivated(e);
            //fg mode
        }

        void App_ProcessException(object sender, DispatcherUnhandledExceptionEventArgs e)
        {
            LogException(e.Exception, "Unhandled Exception have occurred");
            e.Handled = true;
            Application.Current.Shutdown(-1);
        }
        [DebuggerHidden]
        protected override void OnExit(ExitEventArgs e)
        {
            base.OnExit(e);
        }


        #region Help methods
        /// <summary>
        /// needs to return path to standard window %%%user appdata%%% folder
        /// </summary>
        /// <returns></returns>
        private string GetUserAppDataPath()
        {
            string data = "";
            try
            {
                data = AppDomain.CurrentDomain.GetData("DataDirectory") as string;
                if (string.IsNullOrEmpty(data))
                    return GetDataPath();
                else return data;
            }
            catch (Exception ex)
            {
                LogException(ex, "Can't get user app data path..." + CompanyName + "," + ProductName + "," + ProductVersion);
            }
            return null;
        }
        private string GetDataPath()
        {
            string basePath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
            string format = @"{0}";
            if (!string.IsNullOrEmpty(CompanyName)) format += @"\{1}\{2}\{3}";
            else format += @"\{2}\{3}";
            string path = string.Format(CultureInfo.CurrentCulture, format, new object[] { basePath, CompanyName, ProductName, ProductVersion });
            if (!Directory.Exists(path))
            {
                Directory.CreateDirectory(path);
            }
            return path;
        }


        private Settings EnsureSettings()
        {
            var settings = Settings.Default;
            settings.ResultsFolder = EnsurePath(string.IsNullOrEmpty(settings.ResultsFolder) ? "%appdata%Results\\" : settings.ResultsFolder.Trim());
            if (!settings.ResultsFolder.EndsWith("\\")) settings.ResultsFolder = settings.ResultsFolder + "\\";
            settings.LogFolder = EnsurePath(string.IsNullOrEmpty(settings.ResultsFolder) ? "%appdata%Logs\\" : settings.LogFolder.Trim());
            if (!settings.LogFolder.EndsWith("\\")) settings.LogFolder = settings.LogFolder + "\\";
            settings.ActionsFolder = EnsurePath(string.IsNullOrEmpty(settings.ResultsFolder) ? "%root%Actions\\" : settings.ActionsFolder.Trim());
            if (!settings.ActionsFolder.EndsWith("\\")) settings.ActionsFolder = settings.ActionsFolder + "\\";
            settings.ResourceFolder = EnsurePath(string.IsNullOrEmpty(settings.ResultsFolder) ? "%root%Resourses\\" : settings.ResourceFolder.Trim());
            if (!settings.ResourceFolder.EndsWith("\\")) settings.ResourceFolder = settings.ResourceFolder + "\\";
            settings.TestEditorHighlightingTemplate = EnsurePath(string.IsNullOrEmpty(settings.TestEditorHighlightingTemplate) ? "%root%config\\highlighting\\testeditor.xshd" : settings.TestEditorHighlightingTemplate.Trim(),true);            
            settings.ConstantsEditorHighlightingTemplate = EnsurePath(string.IsNullOrEmpty(settings.ConstantsEditorHighlightingTemplate) ? "%root%config\\highlighting\\constantseditor.xshd" : settings.ConstantsEditorHighlightingTemplate.Trim(),true);
            if (string.IsNullOrEmpty(settings.DefaultTestSteps)) settings.DefaultTestSteps = "Navigate, To Url = c:site.url";
            AppSettings = settings;

            LogAppState("TestActions locate:" + AppSettings.ActionsFolder, 1);
            LogAppState("Application is working under Environment:" + AppSettings.Environment, 1);
            LogAppState("Log file could be found:" + settings.LogFolder, 1);
            LogAppState("Test results are storing in:" + settings.ResultsFolder, 1);
            LogAppState("Image Resources locates:" + settings.ResourceFolder, 1);
            LogAppState("Default Test Steps:" + settings.DefaultTestSteps, 1);
            AppSettings.Save();
            return AppSettings;
        }
        private string EnsurePath(string path,bool isfile=false)
        {
            path = Replace((path ?? "").Trim(), Properties[PathReplacements] as Dictionary<string, string>);
            var dirpath=isfile?Path.GetDirectoryName(path):path;
            if (!Directory.Exists(dirpath))
                try
                {
                    Directory.CreateDirectory(path);
                }
                catch (Exception ex)
                {
                    LogException(ex, "Can't create path:" + path);
                }
            return path;
        }
        private string Replace(string source, Dictionary<string, string> replacements)
        {
            if (replacements == null || string.IsNullOrEmpty(source)) return source;
            var result = new StringBuilder(source);
            foreach (var v in replacements)///TODO: regular expression could be used here
                result = result.Replace(v.Key, v.Value);
            return result.ToString();
        }
        private void EnterDebugMode()
        {
            try
            {
                LogAppState("Attaching Debugger...", 1);
                Debugger.Launch();
            }
            catch (Exception ex)
            {
                LogException(ex, "Error!Can't Attach Debugger");
            }
        }
        private void ShowErrorMessage(string message)
        {
            LogException(new Exception(message), "show error message and log");
            MessageBox.Show(message, "Error!", MessageBoxButton.OK, MessageBoxImage.Error);
        }

        private void LogException(Exception ex, string msg)
        {
            LogAppState(FormatException(ex, msg), -1);
        }

        private string FormatException(Exception ex, string msg)
        {
            var sb = new StringBuilder(msg);
            if (!msg.Equals(ex.Message, StringComparison.OrdinalIgnoreCase)) sb.AppendLine("Message:\t\t\t" + ex.Message);
            sb.AppendLine("Source:\t\t\t" + ex.Source);
            sb.AppendLine("StackTrace:\t\t" + ex.StackTrace);
            if (ex.InnerException != null)
                sb.AppendFormat("\tInner Exception:\r\n{0}", FormatException(ex.InnerException, ex.Message));
            return sb.ToString();
        }
        StringBuilder sb = new StringBuilder();
        private void LogAppState(string message, int level)
        {
            if (ContextInstance == null || ContextInstance.Log == null)
                sb.AppendLine("\t\t" + level + "\t\t" + message);
            else
                if (sb.Length > 0)
                {
                    ContextInstance.Log.Info(sb.ToString());
                    sb.Clear();
                }
                else
                {
                    if (level > 0) ContextInstance.Log.Info(message);
                    else if (level < 0) ContextInstance.Log.Error(message);
                    else ContextInstance.Log.Debug(message);
                }
        }
        private string CompanyName;
        private string ProductName;
        public string ProductVersion;
        private Assembly CurAssembly;
        private Settings AppSettings;
        public ITestDataContext ContextInstance;

        #endregion

    }
}
